package channel

import "fmt"

//1.channel是一种类型  引用类型

var ch chan string //声明一个 string类型的chan
var ch1 chan []int //声明一个 int切片类型的chan

func Chan() {
	ch2 := make(chan int)
	//将10发送到通道ch2
	ch2 <- 10
	x := <-ch2
	//从通过ch2接收值x

	//close(ch2)关闭通道
	println(ch, ch2, x)
}

//1.1.4. channel操作
// 1.1.4. channel操作
// 通道有发送（send）、接收(receive）和关闭（close）三种操作。
//关于关闭通道需要注意的事情是，只有在通知接收方goroutine所有的数据都发送完毕的时候才需要关闭通道。
//通道是可以被垃圾回收机制回收的，它和关闭文件是不一样的，在结束操作之后关闭文件是必须要做的，但关闭通道不是必须的

func Chan2() {
	ch := make(chan int) //如何解决无缓冲通道的死锁问题。
	ch <- 1
	fmt.Println("发送成功")
}

func Chan3(c chan int) {

	x := <-c
	fmt.Println("接收成功", x)
}

func Chan4() {
	ch := make(chan int, 2)
	ch <- 10
	fmt.Println("发送数据") //有缓冲通道这里就不会阻塞，造成死锁
}

func GoruntineChan2() {
	ch := make(chan int) //如何解决无缓冲通道的死锁问题。
	go Chan3(ch)
	ch <- 10
	fmt.Println("发送成功")

}

//上面Chan2报错 为什么会出现deadlock错误呢？

// 因为我们使用ch := make(chan int)创建的是无缓冲的通道，无缓冲的通道只有在有人接收值的时候才能发送值。就像你住的小区没有快递柜和代收点，快递员给你打电话必须要把这个物品送到你的手中，简单来说就是无缓冲的通道必须有接收才能发送。

// 上面的代码会阻塞在ch <- 10这一行代码形成死锁，那如何解决这个问题呢？

//无缓冲通道上的发送操作会阻塞，直到另一个goroutine在该通道上执行接收操作，这时值才能发送成功，
// 两个goroutine将继续执行。相反，如果接收操作先执行，接收方的goroutine将阻塞，直到另一个goroutine在该通道上发送一个值。

//使用无缓冲通道进行通信将导致发送和接收的goroutine同步化。因此，无缓冲通道也被称为同步通道。

//close 可以通过内置的close()函数关闭channel（如果你的管道不往里存值或者取值的时候一定记得关闭管道）

func Chanclose() {
	c := make(chan int)
	go func() {
		for i := 0; i < 5; i++ {
			c <- i
		}
		close(c)
	}()
	for {
		if data, ok := <-c; ok {
			fmt.Println(data)
		} else {
			break
		}
	}
	fmt.Println("main结束")
}

//1.1.8. 如何优雅的从通道循环取值
//当通过通道发送有限的数据时，我们可以通过close函数关闭通道来告知从该通道接收值的goroutine停止等待。
//当通道被关闭时，往该通道发送值会引发panic，从该通道里接收的值一直都是类型零值。那如何判断一个通道是否被关闭了呢？

func ChanClose1() {
	ch1 := make(chan int)
	ch2 := make(chan int)
	// 开启goroutine将0~100的数发送到ch1中
	go func() {
		for i := 0; i < 100; i++ {
			ch1 <- i
		}
		close(ch1)
	}()
	// 开启goroutine从ch1中接收值，并将该值的平方发送到ch2中
	go func() {
		for {
			i, ok := <-ch1 // 通道关闭后再取值ok=false
			if !ok {
				break
			}
			ch2 <- i * i
		}
		close(ch2)
	}()
	// 在主goroutine中从ch2中接收值打印
	for i := range ch2 { // 通道关闭后会退出for range循环
		fmt.Println(i)
	}
}

//1.1.9. 单向通道
//有的时候我们会将通道作为参数在多个任务函数间传递，
//很多时候我们在不同的任务函数中使用通道都会对其进行限制，比如限制通道在函数中只能发送或只能接收。

func Counter(out chan<- int) {
	for i := 0; i < 100; i++ {
		out <- i
	}
	close(out)
}

func Squarer(out chan<- int, in <-chan int) {
	for i := range in {
		out <- i * i
	}
	close(out)
}
func Printer(in <-chan int) {
	for i := range in {
		fmt.Println(i)
	}
}

func Chanmains() {
	ch1 := make(chan int)
	ch2 := make(chan int)
	go Counter(ch1)
	go Squarer(ch2, ch1)
	Printer(ch2)
}
